package com.solarStruggle.apps

import java.io.{File, InputStream, FileWriter}
import org.apache.commons.cli.{HelpFormatter, Option => CliOption, Options, OptionBuilder, PosixParser}
import util.{ODS, ODSRow}

/**
 * Utility to generate code based on spreadsheets containing the data
 */
object CodeGen {
	def main(args : Array[String]) {
		parseCommandLine(args) match {
			case Some(runner) => runner.run()
			case None =>
		}
	}

	/**
	 * Parses the command-line strings and returns either a CodeGenRunner, with all options set, or None,
	 * indicating that execution should abort
	 */
	def parseCommandLine(args: Array[String]): Option[CodeGenRunner] = {
		val options = new Options

		options.addOption(new CliOption("d", "data-dir", true, "Directory containing data files"))
		options.addOption(new CliOption("o", "output-dir", true, "Directory to output files to"))
		options.addOption(new CliOption("h", "help", false, "Prints this help"))

		val parser = new PosixParser
		val cmdLine = parser.parse(options, args)

		if(cmdLine.hasOption("help")) {
			new HelpFormatter().printHelp("ShardGen [OPTIONS]", options)
			None
		} else {
			Some(new CodeGenRunner(
				cmdLine.getOptionValue("dataDir", "src/main/resources/codegen"),
				cmdLine.getOptionValue("output-dir", "src/main/scala")))
		}
	}
}

/**
 * Class which does the work of parsing the data for shards
 */
class CodeGenRunner(dataDir: String, outputDir: String) 
extends Runnable {


	override def run() {
		processShards()
		processTeams()
	}

	def processTeams() {
		val odsFileName = dataDir + "/Teams.ods"
		val outputFileName = outputDir + "/com/solarStruggle/domain/stats/TeamStats.scala"
		val odsModificationTime = new File(odsFileName).lastModified

		val ods = new ODS(odsFileName)
		val outputModificationTime = new File(outputFileName).lastModified

		if(outputModificationTime < odsModificationTime) {
			val writer = new FileWriter(outputFileName)

			writer.write("package com.solarStruggle.domain.stats\n")
			writer.write("\n")
			writer.write("import com.solarStruggle.domain.Team\n")
			writer.write("\n")

			for(row <- ods.tables(0).rows) {
				println("Processing team " + row.id + "...")
				writer.write(rowToClass(row, row.id + "Stats", Some("Team"), None, None))
			}

			writer.close()
		} else {
			println("Skipping up-to-date file " + outputFileName)
		}
	}

	def processShards() {
		val odsFileName = dataDir + "/Shards.ods"
		val odsModificationTime = new File(odsFileName).lastModified

		val ods = new ODS(odsFileName)

		for(table <- ods.tables) {
			val shardName = table.name

			val outputFileName = outputDir + "/com/solarStruggle/domain/stats/" + shardName + "Stats.scala"
			val outputModificationTime = new File(outputFileName).lastModified
			if(outputModificationTime < odsModificationTime) {
				val writer = new FileWriter(outputFileName)

				writer.write("package com.solarStruggle.domain.stats\n")
				writer.write("\n")
				writer.write("import com.solarStruggle.domain.{" + shardName + " => " + shardName + "Base}\n")
				writer.write("\n")

				for(row <- table.rows) {
					println("Processing shard " + row.id + "...")
					writer.write(rowToClass(row, row.id + "Stats", Some(shardName), Some(row.id), Some("ShardFactory")))
				}

				writer.close()
			} else {
				println("Skipping up-to-date file " + outputFileName)
			}
		}
	}

	def rowToClass(row: ODSRow, className: String, baseClass: Option[String], objectName: Option[String], objectBase: Option[String]): String = {
		val builder = new StringBuilder()

		builder.append("class " + className + "\n")
		baseClass match {
			case Some(x) => builder.append("extends " + x + " {\n")
			case None => builder.append("{\n")
		}

		for(key <- row.values.keySet) {
			val value = row.values(key)
			val quote = if(value.matches("-?[0-9]+")) "" else "\""
			builder.append("\tdef " + key + " = " + quote + value + quote + "\n")
		}

		builder.append("}\n")
		builder.append("\n")
		objectName match {
			case Some(x) =>
				builder.append("object " + x)
				objectBase match {
					case Some(base) => builder.append(" extends " + base + " ")
					case None =>
				}
				builder.append("{\n")
				builder.append("\toverride def apply() = new " + className + "()\n")
				builder.append("\n")
				builder.append("\toverride lazy val singleton = new " + className + "()\n")
				builder.append("}\n")
				builder.append("\n")
			case None =>
		}
		builder.toString
	}
}
